module PeerCafe
  module Visualization
    class Manager
      include Singleton

      attr_reader :map

      def initialize
        @view = GoSim::View.instance

        @view.add_reset_handler { PeerCafe::Simulation.instance.reset }

        @data = @view.data
        @controls = @view.controls
        @log = @view.log

        @map = @view.space_map
        @nodes = {}

        @edges_visible = true
        @edges = {}

        # Register to handle various datasets
        GoSim::DataSet.add_handler(:topic, &method(:handle_topic))
        GoSim::DataSet.add_handler(:peer, &method(:handle_peer))

        # Add custom controls
        edge_toggle = Gtk::Button.new("Toggle Edges")
        edge_toggle.signal_connect("clicked") do
          @edges_visible = !@edges_visible 
          if @edges_visible
            @edges.values.each {|e| e.show }
          else
            @edges.values.each {|e| e.hide }
          end
        end
        box = Gtk::VBox.new
        box.pack_start(edge_toggle)
        @controls.add(box)
        @controls.show_all
      end

      def handle_topic(nid, status, profile)
        case status
        when :new
          @nodes[nid] = Node.new(self, nid, profile)
        when :family
        end
      end

      def handle_peer(nid, status, *args)
        case status
        when :new
          @edges[nid] = Edge.new(self, 
                                 [@nodes[args[0]].x, @nodes[args[0]].y], 
                                 [@nodes[args[1]].x, @nodes[args[1]].y])
        when :family
          @edges[nid].family
        when :neighbor
          @edges[nid].neighbor
        when :peer
          @edges[nid].peer if @edges.key?(nid)
        when :dead
          @edges[nid].kill
          @edges.delete(nid)
        end
      end
    end

    module VizComponent
      SCALE = 20
      OFFSET = 40
      
      def tx(p)
        return p * SCALE + OFFSET
      end
      alias :ty :tx

    end

    class Edge < Gnome::CanvasGroup
      include VizComponent

      PEER_WIDTH     = 1
      FAMILY_WIDTH   = 2
      NEIGHBOR_WIDTH = 2

      PEER_FILL      = 'green'
      FAMILY_FILL    = 'yellow'
      NEIGHBOR_FILL  = 'red'

      def initialize(manager, p1, p2)
        @manager = manager
        @map = @manager.map
        @p1 = p1.collect {|c| c + Node::SIZE / 2 }
        @p2 = p2.collect {|c| c + Node::SIZE / 2 }

        super(@map.root, :x => 0.0, :y => 0.0) 
        points = [@p1, @p2]
        @line = Gnome::CanvasLine.new(self, 
                                      :points => points,
                                      :width_pixels => PEER_WIDTH,
                                      :fill_color => PEER_FILL)
      end
      
      def neighbor
        set_color(NEIGHBOR_FILL)
        set_width(NEIGHBOR_WIDTH)

      end

      def peer
        set_color(PEER_FILL)
        set_width(PEER_WIDTH)
        raise_to_top
      end

      def family
        set_color(FAMILY_FILL)
        set_width(FAMILY_WIDTH)
      end

      def kill
        hide
      end

      def set_width(width)
        @line.set(:width_pixels => width)
      end

      def set_color(color)
        @line.set(:fill_color => color)
      end
    end

    class Node < Gnome::CanvasGroup
      include VizComponent

      SIZE = 25

      NODE_FILL = 'DeepSkyBlue'
      NODE_SELECTED_FILL = 'red'
      NODE_OUTLINE = 'black'

      LABEL_FILL = 'white'
      BOX_FILL = 'black'
      BOX_OUTLINE = 'darkgray'

      attr_reader :x, :y

      def initialize(manager, id, profile)
        @manager = manager
        @id = id
        @profile = profile
        @map = @manager.map

        @x = tx(profile[0])
        @y = ty(profile[1])

        super(@map.root, :x => @x, :y => @y) 
        @circle = Gnome::CanvasEllipse.new(self, 
              :fill_color => NODE_FILL, :outline_color => NODE_OUTLINE, 
              :x1 => 0, :x2 => SIZE,
              :y1 => 0, :y2 => SIZE)

        mid = SIZE / 2
        @label = Gnome::CanvasText.new(self, 
                                       :x => mid, :y => SIZE + 2, 
                                       :fill_color => LABEL_FILL,
                                       :anchor => Gtk::ANCHOR_NORTH,
                                       :size_points => 15,
                                       :text => "#{@id.to_s}")# (#{@neighbor_locs.size})")
        @label.hide
        @label.text_width
        w = @label.text_width
        @box = Gnome::CanvasRect.new(self, :x1 => mid - (w / 2 + 4), :y1 => SIZE + 2,
                                :x2 => mid + w / 2 + 4,  
                                :y2 => SIZE + @label.text_height + 2,
                                :fill_color => BOX_FILL,
                                :outline_color => BOX_OUTLINE) 
        @box.raise_to_top
        @label.raise_to_top
        @box.hide

        signal_connect("event") do | item, event |
          if event.event_type == Gdk::Event::BUTTON_PRESS && event.button == 1
            @selected ? deselect : select
          end
        end
      end

      def select
        raise_to_top
        @selected = true
        @circle.set(:fill_color => NODE_SELECTED_FILL)
        @label.show
        @box.show
        #draw_links
      end

      def deselect
        @selected = false
        @circle.set(:fill_color => NODE_FILL)
        @label.hide
        @box.hide
        #hide_links
      end

      def refresh
        @label.set(:text => "#{@id.to_s} (#{@neighbor_locs.size})")
=begin
        if @selected
          hide_links
          draw_links
        end
=end
      end

    end
  end
end

PeerCafe::Visualization::Manager.instance
